"""
可视化工具

将数据转换为表格和图表在Chainlit中展示。
"""

import chainlit as cl
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
import logging
from typing import Dict, Any, List, Optional, Union, Literal
from datetime import datetime


# 显示模式类型
ElementDisplay = Literal["inline", "side", "page"]


class Visualizer:
    """数据可视化工具"""
    
    def __init__(self):
        """初始化可视化工具"""
        self.logger = logging.getLogger("ui.visualizer")
    
    async def display_table(
        self,
        df: pd.DataFrame,
        title: str = "",
        display_mode: ElementDisplay = "inline"
    ):
        """
        显示表格
        
        Args:
            df: 要显示的DataFrame
            title: 表格标题
            display_mode: 显示模式（inline或side）
        """
        try:
            if df.empty:
                await cl.Message(content=f"{title}\n\n暂无数据").send()
                return
            
            # 将DataFrame转换为markdown格式
            markdown_table = df.to_markdown(index=False)
            
            # 创建文本元素
            elements = [
                cl.Text(
                    name=title or "数据表格",
                    content=markdown_table,
                    display=display_mode
                )
            ]
            
            # 发送消息
            await cl.Message(
                content=f"### {title}" if title else "### 数据表格",
                elements=elements
            ).send()
            
            self.logger.info(f"显示表格成功: {title}, 行数={len(df)}")
            
        except Exception as e:
            self.logger.error(f"显示表格失败: {e}")
            await cl.Message(content=f"显示表格失败: {str(e)}").send()
    
    async def display_dataframe(
        self,
        data: Union[pd.DataFrame, Dict[str, Any], List[Dict[str, Any]]],
        title: str = "",
        max_rows: int = 50
    ):
        """
        显示DataFrame（支持多种数据格式）
        
        Args:
            data: 数据（DataFrame、字典或字典列表）
            title: 标题
            max_rows: 最大显示行数
        """
        try:
            # 转换为DataFrame
            if isinstance(data, pd.DataFrame):
                df = data
            elif isinstance(data, dict):
                df = pd.DataFrame([data])
            elif isinstance(data, list):
                df = pd.DataFrame(data)
            else:
                await cl.Message(content=f"{title}\n\n不支持的数据格式").send()
                return
            
            # 限制行数
            if len(df) > max_rows:
                df = df.head(max_rows)
                title = f"{title} (仅显示前{max_rows}行)"
            
            await self.display_table(df, title)
            
        except Exception as e:
            self.logger.error(f"显示DataFrame失败: {e}")
            await cl.Message(content=f"显示数据失败: {str(e)}").send()
    
    async def display_chart(
        self,
        data: Dict[str, Any],
        chart_type: str = "bar",
        title: str = "",
        display_mode: ElementDisplay = "inline"
    ):
        """
        显示图表
        
        Args:
            data: 图表数据
            chart_type: 图表类型（bar, line, scatter, pie等）
            title: 图表标题
            display_mode: 显示模式
        """
        try:
            fig = None
            
            if chart_type == "bar":
                fig = go.Figure(data=[
                    go.Bar(
                        x=data.get("x", []),
                        y=data.get("y", []),
                        name=data.get("name", ""),
                        marker_color=data.get("color", "#1f77b4")
                    )
                ])
            
            elif chart_type == "line":
                fig = go.Figure(data=[
                    go.Scatter(
                        x=data.get("x", []),
                        y=data.get("y", []),
                        mode='lines+markers',
                        name=data.get("name", ""),
                        line=dict(color=data.get("color", "#1f77b4"))
                    )
                ])
            
            elif chart_type == "scatter":
                fig = go.Figure(data=[
                    go.Scatter(
                        x=data.get("x", []),
                        y=data.get("y", []),
                        mode='markers',
                        name=data.get("name", ""),
                        marker=dict(
                            size=data.get("size", 10),
                            color=data.get("color", "#1f77b4")
                        )
                    )
                ])
            
            elif chart_type == "pie":
                fig = go.Figure(data=[
                    go.Pie(
                        labels=data.get("labels", []),
                        values=data.get("values", []),
                        hole=data.get("hole", 0)
                    )
                ])
            
            else:
                await cl.Message(content=f"不支持的图表类型: {chart_type}").send()
                return
            
            # 更新布局
            fig.update_layout(
                title=title or data.get("title", ""),
                xaxis_title=data.get("x_label", ""),
                yaxis_title=data.get("y_label", ""),
                height=data.get("height", 400),
                showlegend=data.get("show_legend", True)
            )
            
            # 创建Plotly元素
            elements = [
                cl.Plotly(
                    name=title or "图表",
                    figure=fig,
                    display=display_mode
                )
            ]
            
            # 发送消息
            await cl.Message(
                content=f"### {title}" if title else "",
                elements=elements
            ).send()
            
            self.logger.info(f"显示图表成功: {title}, 类型={chart_type}")
            
        except Exception as e:
            self.logger.error(f"显示图表失败: {e}")
            await cl.Message(content=f"显示图表失败: {str(e)}").send()
    
    async def display_multi_series_chart(
        self,
        data: List[Dict[str, Any]],
        chart_type: str = "line",
        title: str = "",
        x_label: str = "",
        y_label: str = ""
    ):
        """
        显示多系列图表
        
        Args:
            data: 多个系列的数据列表
            chart_type: 图表类型
            title: 图表标题
            x_label: X轴标签
            y_label: Y轴标签
        """
        try:
            fig = go.Figure()
            
            for series in data:
                if chart_type == "line":
                    fig.add_trace(go.Scatter(
                        x=series.get("x", []),
                        y=series.get("y", []),
                        mode='lines+markers',
                        name=series.get("name", ""),
                        line=dict(color=series.get("color"))
                    ))
                elif chart_type == "bar":
                    fig.add_trace(go.Bar(
                        x=series.get("x", []),
                        y=series.get("y", []),
                        name=series.get("name", ""),
                        marker_color=series.get("color")
                    ))
            
            fig.update_layout(
                title=title,
                xaxis_title=x_label,
                yaxis_title=y_label,
                height=400,
                showlegend=True
            )
            
            elements = [cl.Plotly(name=title, figure=fig, display="inline")]
            await cl.Message(content=f"### {title}", elements=elements).send()
            
            self.logger.info(f"显示多系列图表成功: {title}")
            
        except Exception as e:
            self.logger.error(f"显示多系列图表失败: {e}")
            await cl.Message(content=f"显示图表失败: {str(e)}").send()
    
    async def display_bond_basic_info(self, bond_info: Dict[str, Any]):
        """
        显示债券基本信息
        
        Args:
            bond_info: 债券基本信息字典
        """
        try:
            # 构建信息文本
            info_text = f"""
## 📋 债券基本信息

**债券代码**: {bond_info.get('债券代码', 'N/A')}  
**债券简称**: {bond_info.get('债券简称', 'N/A')}  
**正股代码**: {bond_info.get('正股代码', 'N/A')}  
**正股名称**: {bond_info.get('正股名称', 'N/A')}

### 市场行情
- **最新价**: {bond_info.get('最新价', 'N/A')} 元
- **涨跌幅**: {bond_info.get('涨跌幅', 'N/A')}%
- **成交量**: {bond_info.get('成交量', 'N/A')}
- **成交额**: {bond_info.get('成交额', 'N/A')}

### 债券信息
- **发行规模**: {bond_info.get('发行规模', 'N/A')} 亿元
- **剩余规模**: {bond_info.get('剩余规模', 'N/A')} 亿元
- **债券评级**: {bond_info.get('债券评级', 'N/A')}
- **到期时间**: {bond_info.get('到期时间', 'N/A')}

### 转股信息
- **转股价**: {bond_info.get('转股价', 'N/A')} 元
- **转股价值**: {bond_info.get('转股价值', 'N/A')} 元
- **纯债价值**: {bond_info.get('纯债价值', 'N/A')} 元
- **回售触发价**: {bond_info.get('回售触发价', 'N/A')} 元
- **强赎触发价**: {bond_info.get('强赎触发价', 'N/A')} 元
"""
            
            await cl.Message(content=info_text).send()
            self.logger.info(f"显示债券基本信息成功: {bond_info.get('债券代码')}")
            
        except Exception as e:
            self.logger.error(f"显示债券基本信息失败: {e}")
            await cl.Message(content=f"显示债券信息失败: {str(e)}").send()
    
    async def display_bond_analysis_result(self, result: Dict[str, Any]):
        """
        显示债券分析结果
        
        Args:
            result: 分析结果字典
        """
        try:
            # 显示分析结果的各个部分
            results = result.get("results", {})
            
            # 1. 信用分析
            if credit_analysis := results.get("credit_analysis"):
                await cl.Message(content=f"## 🏦 发行人信用分析\n\n{credit_analysis}").send()
            
            # 2. 条款分析
            if terms_analysis := results.get("terms_analysis"):
                await cl.Message(content=f"## 📜 债券条款分析\n\n{terms_analysis}").send()
            
            # 3. 宏观分析
            if macro_analysis := results.get("macro_analysis"):
                await cl.Message(content=f"## 🌍 宏观行业分析\n\n{macro_analysis}").send()
            
            # 4. 价值分析
            if value_analysis := results.get("value_analysis"):
                await cl.Message(content=f"## 💎 相对价值分析\n\n{value_analysis}").send()
            
            # 5. 投资总结
            if final_summary := results.get("final_summary"):
                await cl.Message(content=f"## 📊 投资总结\n\n{final_summary}").send()
            
            self.logger.info(f"显示债券分析结果成功: {result.get('bond_code')}")
            
        except Exception as e:
            self.logger.error(f"显示债券分析结果失败: {e}")
            await cl.Message(content=f"显示分析结果失败: {str(e)}").send()
    
    async def display_comparison_result(self, result: Dict[str, Any]):
        """
        显示债券对比结果
        
        Args:
            result: 对比结果字典
        """
        try:
            bond_codes = result.get("bond_codes", [])
            
            await cl.Message(
                content=f"## 🔄 债券对比分析\n\n对比债券: {', '.join(bond_codes)}"
            ).send()
            
            # 显示对比表格（如果有）
            if "comparison_table" in result:
                await self.display_table(
                    result["comparison_table"],
                    title="债券对比表"
                )
            
            # 显示对比结论
            if comparison_summary := result.get("results", {}).get("comparison_summary"):
                await cl.Message(
                    content=f"### 对比结论\n\n{comparison_summary}"
                ).send()
            
            self.logger.info(f"显示债券对比结果成功: {bond_codes}")
            
        except Exception as e:
            self.logger.error(f"显示债券对比结果失败: {e}")
            await cl.Message(content=f"显示对比结果失败: {str(e)}").send()
    
    async def display_search_results(self, results: List[Dict[str, Any]], query: str):
        """
        显示搜索结果
        
        Args:
            results: 搜索结果列表
            query: 搜索查询
        """
        try:
            if not results:
                await cl.Message(content=f"未找到符合条件的债券: {query}").send()
                return
            
            # 转换为DataFrame并显示
            df = pd.DataFrame(results)
            await self.display_table(
                df,
                title=f"搜索结果 (共{len(results)}条)",
                display_mode="inline"
            )
            
            self.logger.info(f"显示搜索结果成功: {len(results)}条")
            
        except Exception as e:
            self.logger.error(f"显示搜索结果失败: {e}")
            await cl.Message(content=f"显示搜索结果失败: {str(e)}").send()
    
    async def display_error(self, error_message: str, details: Optional[str] = None):
        """
        显示错误信息
        
        Args:
            error_message: 错误消息
            details: 详细信息（可选）
        """
        content = f"## ❌ 错误\n\n{error_message}"
        
        if details:
            content += f"\n\n**详细信息**:\n```\n{details}\n```"
        
        await cl.Message(content=content).send()
        self.logger.error(f"显示错误: {error_message}")
    
    async def display_progress(self, message: str, percentage: Optional[int] = None):
        """
        显示进度信息
        
        Args:
            message: 进度消息
            percentage: 进度百分比（可选）
        """
        if percentage is not None:
            content = f"⏳ {message} ({percentage}%)"
        else:
            content = f"⏳ {message}"
        
        await cl.Message(content=content).send()


# 全局可视化工具实例
visualizer = Visualizer()